home *** CD-ROM | disk | FTP | other *** search
- /*
- * This software is copyright 1992 by Robert Morris.
- * You may freely redistribute this software as shareware
- * if you do so in the same form as you got it. If you find
- * this software useful, please send $12 to:
- * Robert Morris
- * P.O. Box 1044
- * Harvard Square Station
- * Cambridge, MA 02238
- * ecognome@aol.com
- * If you incorporate any of this software in any kind of
- * commercial product, please send $2 per copy distributed
- * to the above address.
- */
-
- /*
- * Decode Group 4 TIFF compression. Based on CCITT Recommendation T.6,
- * "Facsimile Coding Schemes and Coding Control Functions for Group 4
- * Facsimile Apparatus," 1988, Fascicle VII.3.
- */
-
- #include "tiffinfo.h"
- #include "tifft4tables.h"
- #include "bits.h"
-
- struct T6InfoRec{
- TIFFPtr ti;
- struct bits in;
- struct bits out;
- short *reference; /* array of indices of changing elements */
- short *nextref; /* changing elements of this line */
- };
- typedef struct T6InfoRec *T6Info;
-
- OSErr T6Line(T6Info);
- long T4Code(T6Info t6, long color);
- void T6References(T6Info t6, long *b1, long *b2);
- OSErr T6EmitSpan(T6Info t6, long length);
-
- #define T6EOF(t6) TestBitsEOF(&(t6->in))
- #define T6Bit(t6) ReadBit(&(t6->in))
-
- OSErr
- DecodeT6(TIFFPtr ti, char *in, long inlen, char *out, long outlen)
- {
- long rowbytes;
- OSErr err = 0;
- T6Info t6 = 0;
- long y, i;
-
- #if 0
- if(ti->t6Options != 0){
- TIFFError(ti, -1, "\pUnrecognized T6 (Group 4) options.");
- return(-1);
- }
- #endif
-
- if(sizeof(t6->reference[0]) == 0 && ti->imageWidth > 31000){
- TIFFError(ti, -1, "\pGroup 4 image is too wide.");
- return(-1);
- }
-
- if(InitT4Tables() != 0){
- TIFFError(ti, -1, "\pOut of memory for T4 tables.");
- return(-1);
- }
-
- rowbytes = (ti->imageWidth + 7) / 8;
-
- t6 = (T6Info) NewPtr(sizeof(struct T6InfoRec));
- if(t6 == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- goto out;
- }
- t6->ti = ti;
- if(NewBits(&(t6->in), (unsigned char *)in, inlen * 8, ti->fillOrder == 1, 0) < 0)
- goto out;
- if(NewBits(&(t6->out), (unsigned char *)out, outlen * 8, 1, 1) < 0)
- goto out;
- t6->reference = t6->nextref = 0;
- t6->reference = (short *)NewPtr((ti->imageWidth+1) * sizeof(t6->reference[0]));
- t6->nextref = (short *)NewPtr((ti->imageWidth+1) * sizeof(t6->reference[0]));
- if(t6->reference == 0 || t6->nextref == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- goto out;
- }
- t6->reference[0] = ti->imageWidth; /* first reference line is all white */
-
- for(y = 0; y < ti->imageLength; y++){
- err = T6Line(t6);
- if(err != 0)
- goto out;
- }
-
- out:
- FreeT4Tables();
- if(t6->reference)
- DisposPtr(t6->reference);
- if(t6->nextref)
- DisposPtr(t6->nextref);
- if(t6)
- DisposPtr(t6);
- #if 0
- ti->err = 0;
- return(0);
- #else
- return((err != 0 || ti->err != 0) ? -1 : 0);
- #endif
- }
-
-
- /*
- * Decode one scan line. Maintain reference bit-pointer to previous
- * decoded scan line. Pad each output line to a byte boundary.
- */
- OSErr
- T6Line(T6Info t6)
- {
- long pos, savestart;
- long bit, b4;
- long b1, b2;
- long run1, run2;
- long nrp = 0, rp = 0;
- short *nextref = t6->nextref;
- short *reference = t6->reference;
- long a0 = -1;
- long a0color = 0; /* lines are assumed to start out white */
- long width = t6->ti->imageWidth;
-
- savestart = TellBits(&(t6->out));
-
- while(a0 < width){
- if(T6EOF(t6))
- return(-1);
-
- while(rp > 0 && reference[rp] > a0)
- rp -= 1;
-
- /* search forward for the first reference change after a0 */
- while(reference[rp] <= a0)
- rp += 1;
-
- /*
- * Make sure b1 has an opposite color to a0.
- * An odd reference index is a change to white.
- * References[] does not have zero-length runs.
- */
- if(reference[rp] < width && (rp & 1) != a0color){
- rp += 1;
- }
- b1 = reference[rp];
-
- if(reference[rp] < width)
- b2 = reference[rp+1];
- else
- b2 = width;
-
- /*
- * the first count of each line is from position 0, not a0, since
- * a0 starts as -1.
- */
- if(a0 == -1){
- b1 -= 1;
- b2 -= 1;
- }
-
- run1 = run2 = -1;
- b4 = Peek4Bits(&(t6->in));
- switch(b4){
- case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15:
- /* 1: V0 */
- TossBits(&(t6->in), 1);
- run1 = b1 - a0;
- break;
- case 6: case 14:
- /* 011: VR1 */
- TossBits(&(t6->in), 3);
- run1 = b1 - a0 + 1;
- break;
- case 2: case 10:
- /* 010: VL1 */
- TossBits(&(t6->in), 3);
- run1 = b1 - a0 - 1;
- break;
- case 4: case 12:
- /* 001: horizontal mode */
- TossBits(&(t6->in), 3);
- run1 = T4Code(t6, a0color);
- if(run1 < 0)
- return(-1);
- run2 = T4Code(t6, !a0color);
- if(run2 < 0)
- return(-1);
- break;
- case 8:
- /* 0001: pass mode */
- TossBits(&(t6->in), 4);
- run1 = b2 - a0;
- /* but switch back to original color */
- run2 = 0;
- break;
- case 0:
- TossBits(&(t6->in), 4);
- Read2Bits(&(t6->in), bit);
- switch(bit){
- case 3:
- /* 000011: VR2 */
- run1 = b1 - a0 + 2;
- break;
- case 1:
- /* 000010: VL2 */
- run1 = b1 - a0 - 2;
- break;
- case 2:
- bit = T6Bit(t6);
- if(bit){
- /* 0000011: VR3 */
- run1 = b1 - a0 + 3;
- } else {
- /* 0000010: VL3 */
- run1 = b1 - a0 - 3;
- }
- break;
- case 0:
- /* 000000: extension? */
- return(-1);
- }
- break;
- }
-
- if(a0 == -1)
- a0 += 1;
-
- WriteBitSpan(&(t6->out), a0color, run1);
- a0 += run1;
- if(nrp > 0 && run1 == 0)
- nrp -= 1;
- else
- nextref[nrp++] = a0;
-
- if(run2 != -1){
- WriteBitSpan(&(t6->out), !a0color, run2);
- a0 += run2;
- if(nrp > 0 && run2 == 0)
- nrp -= 1;
- else
- nextref[nrp++] = a0;
- } else {
- a0color = !a0color;
- }
-
- /*
- * record the change for the next reference line.
- * eliminate zero-width runs.
- */
- }
-
- if(a0 != width ||
- TellBits(&(t6->out)) - savestart != width){
- TIFFError(t6->ti, -1, "\pBad line length.");
- return(-1);
- }
-
- /* pad output line to a full byte */
- pos = TellBits(&(t6->out));
- while(pos & 7){
- if(WriteBits(&(t6->out), 0, 1) < 0)
- return(-1);
- pos += 1;
- }
-
- nextref[nrp++] = width;
- t6->nextref = reference;
- t6->reference = nextref;
-
- return(0);
- }
-
- #if 0
- /*
- * Emit a run of pixels all of the same color. Add the run length to a0.
- * Invert the a0 color. The first run on each line is from position 0, not -1,
- * even though a0 starts out as -1.
- */
- OSErr
- T6EmitSpan(T6Info t6, long length)
- {
- WriteBitSpan(&(t6->out), t6->a0color, length);
-
- if(t6->a0 == -1)
- t6->a0 += 1;
- t6->a0 += length;
- t6->a0color = !t6->a0color;
-
- /*
- * record the change for the next reference line.
- * eliminate zero-width runs.
- */
- if(t6->nrp > 0 && length == 0)
- t6->nrp -= 1;
- else
- t6->nextref[t6->nrp++] = t6->a0;
-
- #if 0
- if((t6->nrp & 1) != t6->a0color)
- return(-1);
- #endif
-
- #if 0
- if((t6->a0 % t6->ti->imageWidth) != (TellBits(&(t6->out)) % t6->ti->imageWidth))
- return(-1);
- #endif
-
- return(0);
- }
-
- /*
- * Calculate b1 and b2. These are positions in the reference line, whereas
- * a0 is a position in the current line. b1 is the first changing element
- * to the right of a0, and of the opposite color to a0. b2 is the next changing
- * element to the right of b1. b2 is used only in pass mode; maybe we shouldn't
- * compute it always. This code calls the element above a0 on the reference line b0.
- */
- void
- T6References(T6Info t6, long *b1, long *b2)
- {
- short rp = t6->rp;
- short *reference = t6->reference;
-
- while(rp > 0 && reference[rp] > t6->a0)
- rp -= 1;
-
- /* search forward for the first reference change after a0 */
- while(reference[rp] <= t6->a0)
- rp += 1;
-
- /*
- * Make sure b1 has an opposite color to a0.
- * An odd reference index is a change to white.
- * References[] does not have zero-length runs.
- */
- if(reference[rp] < t6->ti->imageWidth && (rp & 1) != t6->a0color){
- rp += 1;
- }
- *b1 = reference[rp];
-
- if(reference[rp] < t6->ti->imageWidth)
- *b2 = reference[rp+1];
- else
- *b2 = t6->ti->imageWidth;
-
- t6->rp = rp;
-
- /*
- * the first count of each line is from position 0, not a0, since
- * a0 starts as -1.
- */
- if(t6->a0 == -1){
- *b1 -= 1;
- *b2 -= 1;
- }
- }
- #endif
-
- /*
- * Read (and consume) one T4 modified Huffman code. Return the length.
- * T6 uses the T4 length codes when all else fails. If there is a makeup
- * code, read it as well as the terminal code.
- */
- long
- T4Code(T6Info t6, long color)
- {
- long bit, state;
- struct t4table *table;
- long len; /* accumulate makeup lengths */
-
- table = &(t4table[color][0]);
- state = 0;
- len = 0;
- while(table[state].terminating == 0){
- if(table[state].makeup){
- /* multiple makeup codes do occur */
- #if 0
- if(len != 0){
- /* multiple makeup codes: are they allowed? */
- return(-1);
- }
- #endif
- len += table[state].on0;
- state = 0;
- }
- if(T6EOF(t6))
- return(-1);
- bit = T6Bit(t6);
- if(bit)
- state = table[state].on1;
- else
- state = table[state].on0;
- if(state == 0 || state >= t4tablelen[color])
- return(-1);
- }
- return(len + table[state].on0);
- }
-